"""
Independent monitoring server for AgentMemory
Loosely coupled design - can be started separately and connected to any agent
"""

from fastapi import FastAPI, HTTPException
from fastapi.responses import HTMLResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import Dict, List, Optional, Any
import json
import asyncio
from datetime import datetime
import threading
import time
import uvicorn

class AgentMemoryMonitor:
    """Independent monitor that can be connected to any AgentMemory instance"""

    def __init__(self):
        self.agent_memory = None
        self.memory_history = []
        self.max_history_entries = 100
        self.start_time = datetime.now()
        self.is_running = False
        self.monitor_lock = threading.Lock()

    def connect_agent_memory(self, agent_memory):
        """Connect to an AgentMemory instance"""
        with self.monitor_lock:
            self.agent_memory = agent_memory
            self.is_running = True
            print(f"[{datetime.now().strftime('%H:%M:%S')}] AgentMemory connected to monitor")

    def disconnect_agent_memory(self):
        """Disconnect from current AgentMemory"""
        with self.monitor_lock:
            self.agent_memory = None
            self.is_running = False
            print(f"[{datetime.now().strftime('%H:%M:%S')}] AgentMemory disconnected from monitor")

    def record_state(self, event_type: str = "update"):
        """Record current memory state"""
        with self.monitor_lock:
            if not self.agent_memory:
                return

            try:
                state = {
                    "timestamp": datetime.now().isoformat(),
                    "event_type": event_type,
                    "goal": getattr(self.agent_memory, 'goal', None),
                    "current_step_id": getattr(self.agent_memory, 'current_step_id', None),
                    "current_thought": getattr(self.agent_memory, 'current_thought', None),
                    "current_action": getattr(self.agent_memory, 'current_action', None),
                    "current_observation": str(getattr(self.agent_memory, 'current_observation', ''))[:200] if hasattr(self.agent_memory, 'current_observation') else None,
                    "iteration_count": getattr(self.agent_memory, 'iteration_count', 0),
                    "max_iteration": getattr(self.agent_memory, 'max_iteration', 10),
                    "error_message": getattr(self.agent_memory, 'error_message', None),
                    "final_answer": getattr(self.agent_memory, 'final_answer', None),
                    "history_length": len(getattr(self.agent_memory, 'history', [])),
                    "reasoning_steps_length": len(getattr(self.agent_memory, 'reasoning_steps', []))
                }

                self.memory_history.append(state)

                # Keep only recent history
                if len(self.memory_history) > self.max_history_entries:
                    self.memory_history = self.memory_history[-self.max_history_entries:]

            except Exception as e:
                print(f"Error recording memory state: {e}")

    def get_current_state(self) -> Dict[str, Any]:
        """Get current memory state"""
        with self.monitor_lock:
            if not self.agent_memory:
                return {"error": "No AgentMemory connected", "status": "disconnected"}

            try:
                # Get context stats if available
                stats = {}
                if hasattr(self.agent_memory, 'context_engine'):
                    try:
                        stats = self.agent_memory.context_engine.get_stats()
                    except:
                        stats = {}

                return {
                    "timestamp": datetime.now().isoformat(),
                    "status": "connected",
                    "goal": getattr(self.agent_memory, 'goal', None),
                    "current_step_id": getattr(self.agent_memory, 'current_step_id', None),
                    "current_thought": getattr(self.agent_memory, 'current_thought', None),
                    "current_action": getattr(self.agent_memory, 'current_action', None),
                    "current_observation": getattr(self.agent_memory, 'current_observation', None),
                    "iteration_count": getattr(self.agent_memory, 'iteration_count', 0),
                    "max_iteration": getattr(self.agent_memory, 'max_iteration', 10),
                    "error_message": getattr(self.agent_memory, 'error_message', None),
                    "final_answer": getattr(self.agent_memory, 'final_answer', None),
                    "history_length": len(getattr(self.agent_memory, 'history', [])),
                    "reasoning_steps_length": len(getattr(self.agent_memory, 'reasoning_steps', [])),
                    "context_stats": stats,
                    "service_uptime": str(datetime.now() - self.start_time)
                }
            except Exception as e:
                return {"error": f"Error getting state: {str(e)}"}

    def get_history(self, limit: int = 10) -> List[Dict[str, Any]]:
        """Get ReAct history"""
        with self.monitor_lock:
            if not self.agent_memory:
                return []

            try:
                history = []
                agent_history = getattr(self.agent_memory, 'history', [])

                for i, (thought, action, observation) in enumerate(agent_history[-limit:], 1):
                    history.append({
                        "step": i,
                        "thought": thought,
                        "action": action,
                        "observation": str(observation)[:300] + "..." if len(str(observation)) > 300 else str(observation)
                    })
                return history
            except Exception as e:
                return [{"error": f"Error getting history: {str(e)}"}]

    def get_insights(self) -> Dict[str, Any]:
        """Get context insights"""
        with self.monitor_lock:
            if not self.agent_memory or not hasattr(self.agent_memory, 'context_engine'):
                return {"error": "Context engine not available"}

            try:
                insights = self.agent_memory.context_engine.curator.insights
                formatted_insights = []

                for i, (trigger, insight) in enumerate(insights.items(), 1):
                    formatted_insights.append({
                        "id": i,
                        "trigger": trigger,
                        "lesson": getattr(insight, 'lesson', ''),
                        "weight": getattr(insight, 'weight', 0.0),
                        "count": getattr(insight, 'count', 0)
                    })
                return {"insights": formatted_insights, "count": len(formatted_insights)}
            except Exception as e:
                return {"error": f"Error getting insights: {str(e)}"}

    def get_timeline(self) -> List[Dict[str, Any]]:
        """Get memory state timeline"""
        with self.monitor_lock:
            return list(self.memory_history)

# Global monitor instance
monitor = AgentMemoryMonitor()

# FastAPI app
app = FastAPI(
    title="AgentMemory Monitor",
    description="Independent monitoring service for AgentMemory - loosely coupled design",
    version="1.0.0"
)

# Enable CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# API Models
class ConnectRequest(BaseModel):
    agent_memory_id: str

class StatusResponse(BaseModel):
    status: str
    message: str

# API Endpoints
@app.get("/", response_class=HTMLResponse)
async def web_interface():
    """Serve the monitoring web interface"""
    return HTMLResponse(content=get_monitor_html(), status_code=200)

@app.post("/api/connect", response_model=StatusResponse)
async def connect_agent():
    """Connect to an AgentMemory instance (manual connection)"""
    return {"status": "ready", "message": "Monitor ready for AgentMemory connection"}

@app.get("/api/state")
async def get_state():
    """Get current AgentMemory state"""
    state = monitor.get_current_state()
    if "error" in state and state["error"] == "No AgentMemory connected":
        raise HTTPException(status_code=503, detail="No AgentMemory connected to monitor")
    return state

@app.get("/api/history")
async def get_history(limit: int = 10):
    """Get ReAct history"""
    return monitor.get_history(limit)

@app.get("/api/insights")
async def get_insights():
    """Get context insights"""
    return monitor.get_insights()

@app.get("/api/timeline")
async def get_timeline():
    """Get memory state timeline"""
    return monitor.get_timeline()

@app.post("/api/record/{event_type}")
async def record_event(event_type: str):
    """Record a memory event"""
    monitor.record_state(event_type)
    return {"status": "recorded", "event_type": event_type}

@app.get("/api/health")
async def health_check():
    """Health check"""
    return {
        "status": "healthy",
        "monitor_running": monitor.is_running,
        "has_agent_memory": monitor.agent_memory is not None,
        "uptime": str(datetime.now() - monitor.start_time),
        "timestamp": datetime.now().isoformat(),
        "timeline_length": len(monitor.memory_history)
    }

# HTML Interface
def get_monitor_html() -> str:
    """Generate HTML interface"""
    return """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AgentMemory Monitor</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            border-radius: 15px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.1);
            overflow: hidden;
        }

        .header {
            background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
            color: white;
            padding: 30px;
            text-align: center;
        }

        .header h1 {
            font-size: 2.5em;
            margin-bottom: 10px;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }

        .header p {
            font-size: 1.1em;
            opacity: 0.9;
        }

        .dashboard {
            padding: 30px;
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 30px;
        }

        .panel {
            background: #f8f9fa;
            border-radius: 10px;
            padding: 25px;
            border-left: 5px solid #3498db;
            box-shadow: 0 5px 15px rgba(0,0,0,0.08);
        }

        .panel h2 {
            color: #2c3e50;
            margin-bottom: 20px;
            font-size: 1.4em;
        }

        .status-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
            margin-bottom: 20px;
        }

        .status-item {
            background: white;
            padding: 15px;
            border-radius: 8px;
            border: 1px solid #e9ecef;
            text-align: center;
        }

        .status-label {
            font-size: 0.9em;
            color: #6c757d;
            margin-bottom: 5px;
        }

        .status-value {
            font-size: 1.3em;
            font-weight: bold;
            color: #2c3e50;
        }

        .current-goal {
            background: white;
            padding: 20px;
            border-radius: 8px;
            border: 1px solid #e9ecef;
            margin-bottom: 20px;
        }

        .current-thought {
            background: #e8f4f8;
            padding: 20px;
            border-radius: 8px;
            border-left: 4px solid #3498db;
            font-style: italic;
            margin-bottom: 20px;
        }

        .history-list {
            max-height: 300px;
            overflow-y: auto;
            background: white;
            border-radius: 8px;
            border: 1px solid #e9ecef;
        }

        .history-item {
            padding: 15px;
            border-bottom: 1px solid #f1f3f4;
        }

        .history-item:last-child {
            border-bottom: none;
        }

        .history-step {
            font-weight: bold;
            color: #3498db;
            margin-bottom: 8px;
        }

        .history-thought {
            color: #2c3e50;
            margin-bottom: 8px;
            font-style: italic;
        }

        .history-action {
            background: #f8f9fa;
            padding: 10px;
            border-radius: 5px;
            font-family: 'Courier New', monospace;
            font-size: 0.9em;
            color: #495057;
            margin-bottom: 8px;
        }

        .history-observation {
            color: #6c757d;
            font-size: 0.9em;
        }

        .error-message {
            background: #f8d7da;
            color: #721c24;
            padding: 15px;
            border-radius: 8px;
            border: 1px solid #f5c6cb;
            margin: 20px 0;
        }

        .loading {
            text-align: center;
            padding: 40px;
            color: #6c757d;
        }

        .full-width {
            grid-column: 1 / -1;
        }

        .refresh-button {
            background: linear-gradient(135deg, #3498db, #2980b9);
            color: white;
            border: none;
            padding: 12px 24px;
            border-radius: 25px;
            cursor: pointer;
            font-size: 1em;
            margin: 20px auto;
            display: block;
            transition: all 0.3s ease;
            box-shadow: 0 4px 15px rgba(52, 152, 219, 0.3);
        }

        .refresh-button:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 20px rgba(52, 152, 219, 0.4);
        }

        @media (max-width: 768px) {
            .dashboard {
                grid-template-columns: 1fr;
                gap: 20px;
                padding: 20px;
            }

            .header h1 {
                font-size: 2em;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>AgentMemory Monitor</h1>
            <p>Independent monitoring service for AgentMemory - Loosely coupled design</p>
        </div>

        <div class="dashboard">
            <div class="panel">
                <h2>Connection Status</h2>
                <div id="connection-status">
                    <div class="loading">Checking connection...</div>
                </div>
            </div>

            <div class="panel">
                <h2>Current Status</h2>
                <div id="current-status">
                    <div class="loading">Loading...</div>
                </div>
            </div>

            <div class="panel full-width">
                <h2>Current Goal & Thought</h2>
                <div id="goal-thought">
                    <div class="loading">Loading...</div>
                </div>
            </div>

            <div class="panel">
                <h2>ReAct History</h2>
                <div id="react-history">
                    <div class="loading">Loading...</div>
                </div>
            </div>

            <div class="panel">
                <h2>Context Insights</h2>
                <div id="context-insights">
                    <div class="loading">Loading...</div>
                </div>
            </div>
        </div>

        <button class="refresh-button" onclick="refreshAllData()">Refresh Data</button>
    </div>

    <script>
        async function fetchData(endpoint) {
            try {
                const response = await fetch(`/api/${endpoint}`);
                if (!response.ok) {
                    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
                }
                return await response.json();
            } catch (error) {
                console.error(`Error fetching ${endpoint}:`, error);
                return { error: error.message };
            }
        }

        function formatConnectionStatus(data) {
            if (data.error) {
                return `<div class="error-message">Disconnected: ${data.error}</div>`;
            }

            const isConnected = data.status !== "disconnected";
            return `
                <div class="status-grid">
                    <div class="status-item">
                        <div class="status-label">Status</div>
                        <div class="status-value">${isConnected ? 'Connected' : 'Disconnected'}</div>
                    </div>
                    <div class="status-item">
                        <div class="status-label">Timeline Events</div>
                        <div class="status-value">${data.timeline_length || 0}</div>
                    </div>
                    <div class="status-item">
                        <div class="status-label">Service Uptime</div>
                        <div class="status-value">${data.uptime || 'Unknown'}</div>
                    </div>
                </div>
            `;
        }

        function formatCurrentStatus(data) {
            if (data.error) {
                return `<div class="error-message">Error: ${data.error}</div>`;
            }

            return `
                <div class="status-grid">
                    <div class="status-item">
                        <div class="status-label">Goal Status</div>
                        <div class="status-value">${data.goal ? 'Active' : 'Idle'}</div>
                    </div>
                    <div class="status-item">
                        <div class="status-label">Current Step</div>
                        <div class="status-value">${data.current_step_id || 'None'}</div>
                    </div>
                    <div class="status-item">
                        <div class="status-label">History Length</div>
                        <div class="status-value">${data.history_length || 0}</div>
                    </div>
                    <div class="status-item">
                        <div class="status-label">Iterations</div>
                        <div class="status-value">${data.iteration_count || 0}/${data.max_iteration || 0}</div>
                    </div>
                </div>
                ${data.error_message ? `<div class="error-message">${data.error_message}</div>` : ''}
                ${data.final_answer ? `<div class="current-goal"><strong>Final Answer:</strong><br>${data.final_answer}</div>` : ''}
            `;
        }

        function formatGoalThought(data) {
            if (data.error) {
                return `<div class="error-message">Error: ${data.error}</div>`;
            }

            return `
                ${data.goal ? `<div class="current-goal"><strong>Current Goal:</strong><br>${data.goal}</div>` : '<div class="current-goal">No active goal</div>'}
                ${data.current_thought ? `<div class="current-thought"><strong>Current Thought:</strong><br>${data.current_thought}</div>` : ''}
                ${data.current_action ? `<div class="current-goal"><strong>Current Action:</strong><br><code>${JSON.stringify(data.current_action, null, 2)}</code></div>` : ''}
            `;
        }

        function formatReactHistory(history) {
            if (!Array.isArray(history) || history.length === 0) {
                return '<div class="current-goal">No history available</div>';
            }

            return `
                <div class="history-list">
                    ${history.map(item => `
                        <div class="history-item">
                            <div class="history-step">Step ${item.step}</div>
                            <div class="history-thought">${item.thought}</div>
                            <div class="history-action">${JSON.stringify(item.action, null, 2)}</div>
                            <div class="history-observation">${item.observation}</div>
                        </div>
                    `).join('')}
                </div>
            `;
        }

        function formatInsights(data) {
            if (data.error) {
                return `<div class="error-message">Error: ${data.error}</div>`;
            }

            if (!data.insights || data.insights.length === 0) {
                return '<div class="current-goal">No insights available</div>';
            }

            return `
                <div class="status-grid">
                    ${data.insights.map(insight => `
                        <div class="status-item">
                            <div class="status-label">${insight.trigger}</div>
                            <div style="margin: 10px 0; color: #2c3e50;">${insight.lesson}</div>
                            <div style="font-size: 0.8em; color: #6c757d;">
                                Weight: ${insight.weight.toFixed(1)} | Count: ${insight.count}
                            </div>
                        </div>
                    `).join('')}
                </div>
            `;
        }

        async function refreshAllData() {
            // Fetch all data in parallel
            const [health, state, history, insights] = await Promise.all([
                fetchData('health'),
                fetchData('state'),
                fetchData('history'),
                fetchData('insights')
            ]);

            // Update UI
            document.getElementById('connection-status').innerHTML = formatConnectionStatus(health);
            document.getElementById('current-status').innerHTML = formatCurrentStatus(state);
            document.getElementById('goal-thought').innerHTML = formatGoalThought(state);
            document.getElementById('react-history').innerHTML = formatReactHistory(history);
            document.getElementById('context-insights').innerHTML = formatInsights(insights);
        }

        // Initialize and start auto-refresh
        refreshAllData();
        setInterval(refreshAllData, 2000); // Refresh every 2 seconds
    </script>
</body>
</html>
    """

def start_monitor_server(host: str = "0.0.0.0", port: int = 8000):
    """Start the independent monitoring server"""
    print(f"[{datetime.now().strftime('%H:%M:%S')}] Starting AgentMemory Monitor on http://{host}:{port}")
    print(f"[{datetime.now().strftime('%H:%M:%S')}] Open browser to http://localhost:{port} to view monitoring interface")
    print(f"[{datetime.now().strftime('%H:%M:%S')}] Use monitor.connect_agent_memory(agent.memory) to connect an agent")

    # Start server in a separate thread
    def run_server():
        uvicorn.run(app, host=host, port=port, log_level="info")

    server_thread = threading.Thread(target=run_server, daemon=True)
    server_thread.start()

    return monitor, server_thread

# Global monitor instance
monitor_instance = None

def get_monitor():
    """Get the global monitor instance"""
    global monitor_instance
    if monitor_instance is None:
        monitor_instance = AgentMemoryMonitor()
    return monitor_instance

if __name__ == "__main__":
    # Start the monitoring server
    monitor, server_thread = start_monitor_server(port=8000)

    # Keep main thread alive
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        print(f"\n[{datetime.now().strftime('%H:%M:%S')}] Shutting down monitoring service...")